本系列文章已出版實體書籍:
「你的地圖會說話?WebGIS 與 JavaScript 的情感交織」(博碩文化)
WebGIS啟蒙首選✖五家地圖API✖近百個程式範例✖實用簡易口訣✖學習難度分級✖補充ES6小知識
本篇文章請搭配
[1-1] 該選哪種地圖API?小孩子才做選擇。
還沒看過的人,傳送門: https://ithelp.ithome.com.tw/articles/10238282
相信大家都有一種經驗,曾經很熟悉的東西,一陣子沒用之後,回來看彷彿恍如隔世。
抑或是曾經的自己寫出來的東西,盯著老半天,卻看不懂到底再寫什麼?
會不會後悔當時的自己沒認真寫註解?或者是懺悔自己命名亂命?
如果程式有好好寫,不用看懂細節,也能夠reuse的話該有多好。
地圖API參數怎麼下,方法怎麼呼叫,忘記怎麼用也沒關係,
人生只要懂過一次,然後記得封裝好會用就好。(誤人子弟
↓ 還記得昨天寫的初始化地圖的API範例嗎?
隨便取一個Google API的初始化地圖,然後包在一個function裡面。
function GMap() {
var GMap = new google.maps.Map(document.getElementById('gmap'), {
center: { lat: 23.5, lng: 121 },
zoom: 7
});
}
↓ 然後設計一個工廠,它可以為我們建立地圖。
function MapFactory() {
this.Build = function (mapType) {
var map;
map = new mapType(); // 地圖的種類可以由參數 mapType 決定
return map;
};
}
↑ MapFactory是一個類別,以這個類別建立的物件會有Build這個方法可以呼叫。
var mapFactory = new MapFactory(); //建立工廠
var gmap = mapFactory.Build(GMap); //新建 GMap 類的地圖
↑ 如此一來就可以順利用我們建立的工廠來新建地圖了,並且可以指定地圖的種類(GMap)。
可是中心點座標、縮放層級等等參數我想要每次呼叫都不一樣耶!那就把參數傳進GMap裡吧!
function GMap(option) { // 傳入一個 option 物件
this.x = option.x; // this 指向當下由 GMap 建立的物件
this.y = option.y;
this.coordinate = option.coordinate;
this.zoom = option.zoom;
this.id = option.id;
this.Init = function () { // 建立一個 Init 方法來初始化地圖
return new google.maps.Map(document.getElementById(this.id), {
center: { lat: this.y, lng: this.x },
zoom: this.zoom
});
};
}
function MapFactory() {
this.Build = function (mapType, option) { // 加入 option 參數
var map;
map = new mapType(option);
return map;
};
}
↑ 工廠建造地圖時加入option參數
var mapFactory = new MapFactory();
var gmap = mapFactory.Build(GMap, {
x: 121,
y: 23.5,
coordinate: 'EPSG3857',
zoom: 7,
id: 'map'
});
gmap.Init();
↑ 呼叫時以物件方式傳入參數
看膩了Google Map,換Leaflet來寫寫看吧!
function LMap(option) {
this.Init = function () {
var LMap = L.map(document.getElementById(this.id), {
center: [this.y, this.x],
zoom: this.zoom,
crs: L.CRS[this.coordinate],
});
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
id: 'mapbox.streets'
}).addTo(LMap);
return LMap;
};
}
這次我們把option在MapFactory呼叫Build時賦予屬性,這樣就不會在GMap寫一次、LMap也寫一次,
造成 重複的程式碼(Duplicate Code)。
function MapFactory() {
this.Build = function (mapType, option) {
var map;
if (!(mapType instanceof Function)) {
console.error(`${mapType} is not constructor.`);
return;
}
map = new mapType(option);
map.x = option.x;
map.y = option.y;
map.coordinate = option.coordinate;
map.zoom = option.zoom;
map.id = option.id;
return map;
};
}
instanceof
這裡有一個小技巧!如果mapType傳入的並不是一個function怎麼辦呢?
可以用instanceof這個語法判斷物件是否為類別的實例。
<<物件>> instanceof <<類別>>
如果這個物件是由這個類別創造出來的則會回傳true,反之,則為false。
注意!instanceof 只適用於JS物件型別,原始型別請使用typeof判斷。
樣板語法 template literal${mapType}
反斜線是ES6才有的樣板語法,並且可以用${...}的方式插入變數,
在ES5以前必須用單引號跟雙引號的組合,並且用字串相加的方式。
如果今天你要建立的地圖物件很多,每一次建立地圖物件的時候,每一次都要賦予該物件Init方法,
可不可以只賦予一次?當然可以!我們把方法建立在它的原型上!
這次換換口味,用ArcGIS API。
function AMap(option) { }
AMap.prototype.Init = function () {
var el = this; // this 指向呼叫 Init 方法的物件
var mapObject = {};
require(["esri/Map", "esri/views/MapView"], function (Map, MapView) {
var myMap = new Map({
basemap: "streets-vector"
});
// 2D viewing
var view = new MapView({
container: el.id,
map: myMap,
zoom: el.zoom,
center: [el.x, el.y]
});
console.log(this); // this 指向 window
mapObject = view;
});
return mapObject;
}
var XXCompany = new MapFactory(); // XXCompany 填你公司的名子
// 以後只需要這樣呼叫,由第一個參數決定要呼叫的地圖API,第二個參數作地圖設定
var map = XXCompany.Build(AMap, {
x: 121,
y: 23.5,
coordinate: 'EPSG3857',
zoom: 7,
id: 'map'
});
map.Init();
然後一樣的呼叫方式,把變數名稱換成你的名子或是公司的名子,
看起來很像某某公司建立的地圖,假裝很厲害?!
講了那麼多,利用簡單工廠模式封裝地圖究竟有什麼優點?
對多種地圖API進行封裝,只要換個參數,就可以隨意切換想用的地圖API
把地圖設定藉由物件的型式傳入function,而不是寫死在程式中,統一介面,更方便進行修改。
每個建立的物件如果有不同的屬性或方法則寫在建構函式(GMap、LMap、AMap)中,相同的屬性或方法則寫在原型(prototype)上。
你可以
在客戶很GY地說要改圖台底層的時候,向老闆解釋有多難多難,向客戶解釋要花多大的人力與時間來修改,結果你只是去改個設定就解決!你可以
在不懂WebGIS的同事請你幫忙寫圖台一堆功能的時候,呼叫幾個寫好的函式new MapFactory(); mapFactory.Build(); map.Init();,三兩下解決同事的請求!你還可以
在IT邦幫忙發發文章,誤人子弟 (X
下一篇,會開始介紹WebGIS的點、線、面資料,
想要知道怎麼用瀏覽器定位嗎? 想要知道怎麼用地址定位搜尋嗎?
請不要錯過。